/*
 * ArchivoPunteros.cpp
 *
 *  Created on: 01/06/2013
 *      Author: jonathan
 */

#include "ArchivoPunteros.h"
#include "../../commons/ArchivoUtils.h"
#include "../../commons/PathsStore.h"
#include "../../commons/InfoRepositorioActual.h"
#include "../estructuras/DocumentoProxy.h"

ArchivoPunteros::ArchivoPunteros(FileName archivo, fstream::openmode mode) {
	this->bs.open(archivo, mode);
	this->compresor = new Compresor(&(this->bs));
}

ArchivoPunteros::ArchivoPunteros(fstream::openmode mode) {
	PathsStore ps;
	this->bs.open(ps.getPathPunteros(), mode);
	this->compresor = new Compresor(&(this->bs));
	this->bs.seekg(0);
}

ArchivoPunteros::~ArchivoPunteros() {
	this->close();
	delete this->compresor;
	this->compresor = 0;
	//me deshago de todos los proxys que puedan estar vivios.
	for (list<PosicionesPoxy*>::iterator it = this->proxysGenerados.begin(); it != proxysGenerados.end(); ++it) {
		delete (*it);
	}
}

PunteroPosiciones ArchivoPunteros::escribir(list<Documento*> &docs, EnteroLargo b) {
	PunteroPosiciones posicion = this->bs.tellg();

	list<Documento*>::iterator itDocs;
	EnteroLargo anterior = 0;

	EnteroLargo nroEscribir;
	EnteroLargo corrimiento = 1;
	//si la frecuencia del termino es menor que la cantidad de documentos los escribo sino, no hace falta.
	if (docs.size() < InfoRepositorioActual::Instance()->getCantidadArchivos()) {
		//guardo todos los documentos en distancias.
		for (itDocs = docs.begin(); itDocs != docs.end(); ++itDocs) {
			nroEscribir = (*itDocs)->getNumero()- anterior;
			// lo corro en 1 para el caso de cero.
			this->compresor->escribirGolomb( nroEscribir + corrimiento, b);
			anterior  = (*itDocs)->getNumero();
			corrimiento = 0;

		}
		this->bs.fillByte();
	}
	//cierro el byte para arrancar uno nuevo con las posiciones.
	list<EnteroLargo>::iterator itPos;
	// ahora escribo todas las posiciones en distancias.
	for (itDocs = docs.begin(); itDocs != docs.end(); ++itDocs) {
		//cuantas posiciones.
		this->compresor->escribirDelta((*itDocs)->getPosiciones()->size());
		anterior = 0;
		//escribo las posiciones
		corrimiento = 1;
		for (itPos = (*itDocs)->getPosiciones()->begin(); itPos != (*itDocs)->getPosiciones()->end(); ++itPos) {
			this->compresor->escribirDelta((*itPos + corrimiento) - anterior);
			anterior = *itPos;
			corrimiento = 0;
		}
	}
	//cierro el byte.
	this->bs.fillByte();

	return posicion;
}

/**
 * Lee los documentos y almacena el offset de a donde ir a buscar las posiciones
 */
list<Documento*> * ArchivoPunteros::leerDocumentos(EnteroLargo offset, EnteroLargo cantArchivos, EnteroLargo b) {

	this->bs.seekg(offset);
	list<Documento*> * lista = new list<Documento*> ;
	DocumentoProxy * doc = NULL;
	EnteroLargo dist = 0;
	EnteroLargo nroDoc;
	PosicionesPoxy * proxy = new PosicionesPoxy(this, cantArchivos);
	EnteroLargo corrimiento = 1;
	if (InfoRepositorioActual::Instance()->getCantidadArchivos() == cantArchivos) {
		int secProxy = 0;
		for (EnteroLargo i = 0; i < cantArchivos; i++) {
			//inicia con datos para poder recuperar las posiciones.
			doc = new DocumentoProxy(proxy, secProxy);
			secProxy++;
			doc->setNumero(i);
			lista->push_back(doc);
		}
	} else {
		EnteroLargo pos = 0;
		for (EnteroLargo i = 0; i < cantArchivos; i++) {
			doc = new DocumentoProxy(proxy, i);
			pos = this->compresor->leerGolomb(b) - corrimiento;
			nroDoc = (pos + dist);
			doc->setNumero(nroDoc);
			dist = nroDoc;
			lista->push_back(doc);
			corrimiento = 0;
		}
		bs.omitByte();
	}

	//aca el bs quedo con la posicion donde arrancan las posiciones.
	proxy->setOffset(bs.tellg());
	//me guardo el proxy para poder destruirlo dsps.
	this->proxysGenerados.push_back(proxy);
	return lista;
}

void ArchivoPunteros::close() {
	this->bs.close();
}

void ArchivoPunteros::posicionar(PunteroPosiciones pos) {
	this->bs.seekg(pos);
}
void ArchivoPunteros::posicionar(streampos pos) {
	this->bs.seekg(pos);
}
//Lee una lista de posiciones a partir de la posicion actual, hace falta un seekg valido antes de empezar a usar esto.
list<EnteroLargo> * ArchivoPunteros::leerListaPosiciones() {
	EnteroLargo cantPosiciones = this->compresor->leerDelta();
	list<EnteroLargo> * lista = new list<EnteroLargo> ();
	EnteroLargo anterior = 0;
	EnteroLargo corrimiento = 1;
	//leo la lista de distancias y la reestablezco.
	for (EnteroLargo i = 0; i < cantPosiciones; i++) {
		anterior = this->compresor->leerDelta() + anterior - corrimiento;
		lista->push_back(anterior);
		corrimiento = 0;
	}
	return lista;
}
